#include <iostream>
#include <string>
#include <map>
#include <unistd.h>
#include <sys/epoll.h>

#include "./sock.hpp"

#define SIZE 128
#define NUM 64

static std::map<std::string, std::string> dict = {{"hello", "你好"}};

void usage(std::string proc)
{
    std::cout << "usage: " << proc << " + " << "port" << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        usage(argv[0]);
        exit(1);
    }

    //1.创建TCP监听socket
    uint16_t port = (uint16_t)atoi(argv[1]);
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock, 5);

    //2.创建epoll模型,获得epoll_fd(文件描述符)
    int epoll_fd = epoll_create(SIZE);
    if(epoll_fd < 0)
    {
        std::cerr << "epoll_create fail!" << std::endl;
    }

    //3.先将listen_sock和它所关心的事件,添加到内核
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;

    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &ev);

    volatile bool quit = false;
    struct epoll_event revs[NUM];

    //4.事件循环
    while(!quit)
    {
        int timeout = -1;
        //这里传入的数组,仅仅是尝试从内核中拿回来已经就绪的事件
        int n = epoll_wait(epoll_fd, revs, NUM, timeout);
        switch(n)
        {
            case -1:
                std::cerr << "epoll_wait failed!" << std::endl;
                break;
            case 0:
                std::cout << "epoll_wait timeout!" << std::endl;
                break;
            default:
                std::cout << "有事件就绪了!" << std::endl;

                //5.处理就绪事件
                for (int i = 0; i < n; ++i)
                {
                    //这个data数据是用户自己定义的,在这里的用处就是可以知道有哪些的fd的事件就绪了
                    int sock = revs[i].data.fd;
                    std::cout << "文件描述符: " << sock << "上面有事件就绪了!" << std::endl;
                    if (revs[i].events & EPOLLIN)
                    {
                        //处理读事件
                            std::cout << "文件描述符:" << sock << "上面有链接事件就绪了!" << std::endl;
                        if (sock == listen_sock)
                        {
                            //处理链接事件
                            int fd = Sock::Accept(listen_sock);
                            if (fd >= 0)
                            {
                                //不能立即读取
                                struct epoll_event ev;
                                ev.events = EPOLLIN;
                                ev.data.fd = fd;
                                //将新的fd托管给epoll
                                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
                                std::cout << "已经将文件描述符" << fd << "托管给epoll了!" << std::endl; 
                            }
                        }
                        else
                        {
                            //处理正常的读取事件
                            std::cout << "文件描述符:" << sock << "上面有读取事件就绪了!" << std::endl;
                            char buffer[1024] = {0};
                            ssize_t ret = recv(sock, buffer, sizeof(buffer)- 1, 0);
                            if (ret > 0)
                            {
                                buffer[ret] = '\0';
                                std::cout << "client[" << sock << "]say:" << buffer << std::endl;

                                if (dict.find("hello") != dict.end())
                                {
                                    strcpy(buffer, dict["hello"].c_str());
                                }

                                send(sock, buffer, sizeof(buffer), 0);

                            }
                            else if (ret == 0)
                            {
                                //对端关闭链接
                                std::cout << "client[" << sock <<"]quit!" << std::endl;
                                close(sock);
                                epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sock, nullptr);
                                std::cout << "sock:[" << sock << "] delete fromepoll success!" << std::endl;
                            }
                            else
                            {
                                //读取失败
                                std::cout << "recv error!" << std::endl;
                                close(sock);
                                epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sock, nullptr);
                                std::cout << "sock:[" << sock << "] delete fromepoll success!" << std::endl;
                            }
                        }
                    }
                    else if (revs[i].events & EPOLLOUT)
                    {
                        //处理写事件
                    }
                    else
                    {
                        //TODO
                    }
                }
                break;
        }
    }

    close(epoll_fd);
    close(listen_sock);

    return 0;
}
